import requests

from . import storage


class Core(object):
    def __init__(self):
        """ init 是 core.py 中定义的唯一方法
            alive 是显示核心是否正在运行的值
                - 您应该调用 Logout 方法进行更改
                - 注销后，核心对象可以再次登录
            仅使用基本的 python 类型
                - 因此，对于高级用途，请自己继承它
            receivingRetryCount 用于接收循环重试
                - 现在是 5 个，但实际上即使是 1 个也足够了
                - 失败就是失败
        """
        self.alive, self.isLogging = False, False
        self.storageClass = storage.Storage(self)
        self.memberList = self.storageClass.memberList
        self.mpList = self.storageClass.mpList
        self.chatroomList = self.storageClass.chatroomList
        self.msgList = self.storageClass.msgList
        self.loginInfo = {}
        self.s = requests.Session()
        self.uuid = None
        self.functionDict = {'FriendChat': {}, 'GroupChat': {}, 'MpChat': {}}
        self.useHotReload, self.hotReloadDir = False, 'itchat.pkl'
        self.receivingRetryCount = 5

    def login(self, enableCmdQR=False, picDir=None, qrCallback=None,
              loginCallback=None, exitCallback=None):
        """ 像网络微信一样登录
            登录
                - 将下载并打开二维码
                - 然后记录扫描状态，暂停以确认
                - 最后它登录并显示您的昵称
            选项
                - enableCmdQR：在命令行中显示二维码
                    - 整数可用于适应奇怪的字符长度
                - picDir：存放二维码的地方
                - qrCallback: 应接受 UUID、状态、QRCODE 的方法
                - loginCallback: 登录成功后回调
                    - 如果未设置，则清除屏幕并删除二维码
                - exitCallback: 注销后回调
                    - 它包含注销的调用
            使用用法
                ..code::python

                    import itchat
                    itchat.login()

            它在 components/login.py 中定义
            当然，登录中的每一个动作都可以在外部调用
                - 您可以扫描源代码以查看如何
                - 并根据自己的需求进行修改
        """
        raise NotImplementedError()

    def get_QRuuid(self):
        """ 获取 QRCODE 的 UUID
            UUID 是 QRCode 的符号
                - 要登录，您需要先获取 UUID
                - 要下载QRCode，您需要将UUID传递给它
                - 为了检查登录状态，还需要 UUID
            如果 UUID 已超时，请再获取另一个
            它在 components/login.py 中定义
        """
        raise NotImplementedError()

    def get_QR(self, uuid=None, enableCmdQR=False, picDir=None, qrCallback=None):
        """ 下载并显示二维码
            选项
                - UUID：如果未设置 UUID，将使用您获取的最新 UUID
                - enableCmdQR：在 cmd 中显示二维码
                - picDir：存储二维码的位置
                - qrCallback：应该接受 uuid、status、qrcode 的方法
            它在 components/login.py 中定义
        """
        raise NotImplementedError()

    def check_login(self, uuid=None):
        """ 检查登录状态
            对于选项：
                - UUID：如果未设置 UUID，将使用您获取的最新 UUID
            对于返回值：
                - 将返回一个字符串
                - 返回值的含义
                    - 200：登录成功
                    - 201： 等待按确认
                    - 408： UUID 超时
                    - 0：未知错误
            用于处理：
                - syncUrl 和 fileUrl 已设置
                - 设置了 BaseRequest
            阻止，直到达到上述任何状态
            它在 components/login.py 中定义
        """
        raise NotImplementedError()

    def web_init(self):
        """ 获取初始化所需的信息
            用于处理：
                - 设置了自己的帐户信息
                - inviteStartCount 已设置
                - syncKey 已设置
                - 部分联系人被获取
            它在 components/login.py 中定义
        """
        raise NotImplementedError()

    def show_mobile_login(self):
        """ 显示网页微信登录标志
            标志在手机微信的顶部
            即使不调用此函数，也会在一段时间后添加符号
            它在 components/login.py 中定义
        """
        raise NotImplementedError()

    def start_receiving(self, exitCallback=None, getReceivingFnOnly=False):
        """ 打开心形循环和接收消息的线程
            选项:
                - exitCallback：注销后回调
                    - 它包含注销的调用
                - getReceivingFnOnly: 如果 True 不会创建和启动线程。相反，将返回 receive fn.
            用于处理:
                - messages: 消息被格式化并传递给已注册的 fns
                - contact : 收到相关信息后，聊天室会更新
            它在 components/login.py 中定义
        """
        raise NotImplementedError()

    def get_msg(self):
        """ fetch messages
            for fetching
                - method blocks for sometime until
                    - new messages are to be received
                    - or anytime they like
                - SyncKey 使用返回的 SyncCheckKey 进行更新
            it is defined in components/login.py
        """
        raise NotImplementedError()

    def logout(self):
        """ 注销
            如果核心现在处于活动状态
                注销会告诉微信后台注销
            Core 已准备好再次登录
            它在 components/login.py 中定义
        """
        raise NotImplementedError()

    def update_chatroom(self, userName, detailedMember=False):
        """ update chatroom
            for chatroom contact
                - a chatroom contact need updating to be detailed
                - detailed means members, encryid, etc
                - auto updating of heart loop is a more detailed updating
                    - member uin will also be filled
                - once called, updated info will be stored
            for options
                - userName: 'UserName' key of chatroom or a list of it
                - detailedMember: whether to get members of contact
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def update_friend(self, userName):
        """ update chatroom
            for friend contact
                - once called, updated info will be stored
            for options
                - userName: 'UserName' key of a friend or a list of it
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def get_contact(self, update=False):
        """ fetch part of contact
            for part
                - all the massive platforms and friends are fetched
                - if update, only starred chatrooms are fetched
            for options
                - update: if not set, local value will be returned
            for results
                - chatroomList will be returned
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def get_friends(self, update=False):
        """ fetch friends list
            for options
                - update: if not set, local value will be returned
            for results
                - a list of friends' info dicts will be returned
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def get_chatrooms(self, update=False, contactOnly=False):
        """ fetch chatrooms list
            for options
                - update: if not set, local value will be returned
                - contactOnly: if set, only starred chatrooms will be returned
            for results
                - a list of chatrooms' info dicts will be returned
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def get_mps(self, update=False):
        """ fetch massive platforms list
            for options
                - update: if not set, local value will be returned
            for results
                - a list of platforms' info dicts will be returned
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def set_alias(self, userName, alias):
        """ set alias for a friend
            for options
                - userName: 'UserName' key of info dict
                - alias: new alias
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def set_pinned(self, userName, isPinned=True):
        """ set pinned for a friend or a chatroom
            for options
                - userName: 'UserName' key of info dict
                - isPinned: whether to pin
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def accept_friend(self, userName, v4, autoUpdate=True):
        """ accept a friend or accept a friend
            for options
                - userName: 'UserName' for friend's info dict
                - status:
                    - for adding status should be 2
                    - for accepting status should be 3
                - ticket: greeting message
                - userInfo: friend's other info for adding into local storage
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def get_head_img(self, userName=None, chatroomUserName=None, picDir=None):
        """ place for docs
            for options
                - if you want to get chatroom header: only set chatroomUserName
                - if you want to get friend header: only set userName
                - if you want to get chatroom member header: set both
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def create_chatroom(self, memberList, topic=''):
        """ create a chatroom
            for creating
                - its calling frequency is strictly limited
            for options
                - memberList: list of member info dict
                - topic: topic of new chatroom
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def set_chatroom_name(self, chatroomUserName, name):
        """ set chatroom name
            for setting
                - it makes an updating of chatroom
                - which means detailed info will be returned in heart loop
            for options
                - chatroomUserName: 'UserName' key of chatroom info dict
                - name: new chatroom name
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def delete_member_from_chatroom(self, chatroomUserName, memberList):
        """ deletes members from chatroom
            for deleting
                - you can't delete yourself
                - if so, no one will be deleted
                - strict-limited frequency
            for options
                - chatroomUserName: 'UserName' key of chatroom info dict
                - memberList: list of members' info dict
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def add_member_into_chatroom(self, chatroomUserName, memberList,
                                 useInvitation=False):
        """ add members into chatroom
            for adding
                - you can't add yourself or member already in chatroom
                - if so, no one will be added
                - if member will over 40 after adding, invitation must be used
                - strict-limited frequency
            for options
                - chatroomUserName: 'UserName' key of chatroom info dict
                - memberList: list of members' info dict
                - useInvitation: if invitation is not required, set this to use
            it is defined in components/contact.py
        """
        raise NotImplementedError()

    def send_raw_msg(self, msgType, content, toUserName):
        """ 许多消息都是以通用方式发送的
            for demo
                .. code:: python

                    @itchat.msg_register(itchat.content.CARD)
                    def reply(msg):
                        itchat.send_raw_msg(msg['MsgType'], msg['Content'], msg['FromUserName'])

            there are some little tricks here, you may discover them yourself
            but remember they are tricks
            it is defined in components/messages.py
        """
        raise NotImplementedError()

    def send_msg(self, msg='Test Message', toUserName=None):
        """ send plain text message
            for options
                - msg: should be unicode if there's non-ascii words in msg
                - toUserName: 'UserName' key of friend dict
            it is defined in components/messages.py
        """
        raise NotImplementedError()

    def upload_file(self, fileDir, isPicture=False, isVideo=False,
                    toUserName='filehelper', file_=None, preparedFile=None):
        """ 将文件上传到服务器并获取 mediaId
            for options
                - fileDir: dir for file ready for upload
                - isPicture: whether file is a picture
                - isVideo: whether file is a video
            for return values
                will return a ReturnValue
                if succeeded, mediaId is in r['MediaId']
            it is defined in components/messages.py
        """
        raise NotImplementedError()

    def send_file(self, fileDir, toUserName=None, mediaId=None, file_=None):
        """ send attachment
            for options
                - fileDir: dir for file ready for upload
                - mediaId: mediaId for file.
                    - if set, file will not be uploaded twice
                - toUserName: 'UserName' key of friend dict
            it is defined in components/messages.py
        """
        raise NotImplementedError()

    def send_image(self, fileDir=None, toUserName=None, mediaId=None, file_=None):
        """ send image
            for options
                - fileDir: dir for file ready for upload
                    - if it's a gif, name it like 'xx.gif'
                - mediaId: mediaId for file.
                    - if set, file will not be uploaded twice
                - toUserName: 'UserName' key of friend dict
            it is defined in components/messages.py
        """
        raise NotImplementedError()

    def send_video(self, fileDir=None, toUserName=None, mediaId=None, file_=None):
        """ send video
            for options
                - fileDir: dir for file ready for upload
                    - if mediaId is set, it's unnecessary to set fileDir
                - mediaId: mediaId for file.
                    - if set, file will not be uploaded twice
                - toUserName: 'UserName' key of friend dict
            it is defined in components/messages.py
        """
        raise NotImplementedError()

    def send(self, msg, toUserName=None, mediaId=None):
        """ 所有发送函数的包装函数
            选项
                - 消息：消息以不同的字符串开头，表示不同的类型
                    - 字符串类型列表：['@fil@'， '@img@'， '@msg@'， '@vid@']
                    - 它们适用于文件、图像、纯文本、视频
                    - 如果它们都不匹配，它将像纯文本一样发送
                - toUserName： 好友字典的'UserName'键
                - mediaId：如果设置，上传将不再重复
            它在 components/messages.py 中定义
        """
        raise NotImplementedError()

    def revoke(self, msgId, toUserName, localId=None):
        """ 使用 its 和 msgId 撤销消息
            选项
                - msgId：服务器上的消息 ID
                - toUserName： 好友字典的'UserName'键
                - localId：本地的消息 ID（可选）
            它在 components/messages.py 中定义
        """
        raise NotImplementedError()

    def dump_login_status(self, fileDir=None):
        """ 将登录状态转储到特定文件
            for option
                - fileDir: dir for dumping login status
            it is defined in components/hotreload.py
        """
        raise NotImplementedError()

    def load_login_status(self, fileDir,
                          loginCallback=None, exitCallback=None):
        """ 从特定文件加载登录状态
            for option
                - fileDir: file for loading login status
                - loginCallback: callback after successfully logged in
                    - if not set, screen is cleared and qrcode is deleted
                - exitCallback: callback after logged out
                    - it contains calling of logout
            it is defined in components/hotreload.py
        """
        raise NotImplementedError()

    def auto_login(self, hotReload=False, statusStorageDir='itchat.pkl',
                   enableCmdQR=False, picDir=None, qrCallback=None,
                   loginCallback=None, exitCallback=None):
        """ 像网络微信一样登录
            登录
                - 将下载并打开二维码
                - 然后记录扫描状态，暂停以确认
                - 最后它登录并显示您的昵称
            选项
                - hotReload：启用热重载
                - statusStorageDir：用于存储登录状态的目录
                - enableCmdQR：在命令行中显示二维码
                    - 整数可用于适应奇怪的字符长度
                - picDir：存储二维码的地方
                - loginCallback：登录成功后的回调
                    - 如果未设置，则清除屏幕并删除二维码
                - exitCallback：注销后回调
                    - 它包含注销的调用
                - qrCallback：应该接受 uuid、status、qrcode 的方法
            for usage
                ..code::python

                    import itchat
                    itchat.auto_login()

            it is defined in components/register.py
            当然，登录中的每一个动作都可以在外部调用
                - 您可以扫描源代码以查看如何操作
                - 并根据您自己的需求进行修改
        """
        raise NotImplementedError()

    def configured_reply(self):
        """ determine the type of message and reply if its method is defined
            however, I use a strange way to determine whether a msg is from massive platform
            I haven't found a better solution here
            The main problem I'm worrying about is the mismatching of new friends added on phone
            If you have any good idea, pleeeease report an issue. I will be more than grateful.
        """
        raise NotImplementedError()

    def msg_register(self, msgType,
                     isFriendChat=False, isGroupChat=False, isMpChat=False):
        """ a decorator constructor
            return a specific decorator based on information given
        """
        raise NotImplementedError()

    def run(self, debug=True, blockThread=True):
        """ 启动自动响应
            对于选项
                - 调试：如果设置，调试信息将显示在屏幕上
            它在 components/register.py 中定义
        """
        raise NotImplementedError()

    def search_friends(self, name=None, userName=None, remarkName=None, nickName=None,
                       wechatAccount=None):
        return self.storageClass.search_friends(name, userName, remarkName,
                                                nickName, wechatAccount)

    def search_chatrooms(self, name=None, userName=None):
        return self.storageClass.search_chatrooms(name, userName)

    def search_mps(self, name=None, userName=None):
        return self.storageClass.search_mps(name, userName)
